package com.ioc.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class CollectionUtils {

    public static <T, U extends Comparable<? super U>> List<T> sortedReversed(List<T> list, Function<? super T, ? extends U> keyExtractor) {
        if (CollectionUtil.isEmpty(list)) {
            return CollectionUtil.newArrayList();
        }
        return list.stream().sorted(Comparator.comparing(keyExtractor).reversed()).collect(Collectors.toList());
    }

    public static <T, U extends Comparable<? super U>> List<T> sorted(List<T> list, Function<? super T, ? extends U> keyExtractor) {
        if (CollectionUtil.isEmpty(list)) {
            return CollectionUtil.newArrayList();
        }
        return list.stream().sorted(Comparator.comparing(keyExtractor)).collect(Collectors.toList());
    }

    public static BigDecimal computeTotal(List<BigDecimal> list) {
        return list.stream().reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    public static BigDecimal computeTotal(List<BigDecimal> list, int scale) {
        if (CollectionUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return list.stream().reduce(BigDecimal.ZERO, BigDecimal::add).setScale(scale, RoundingMode.HALF_UP);
    }

    public static <T> BigDecimal computeTotal(List<T> list, Function<? super T, BigDecimal> mapper) {
        if (CollectionUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return list.stream().map(mapper).reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    public static <T> BigDecimal computeTotal(List<T> list, Function<? super T, BigDecimal> mapper, int scale) {
        if (CollectionUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return computeTotal(list, mapper).setScale(scale, RoundingMode.HALF_UP);
    }


    public static <T, R> BigDecimal computeMapperTotal(List<T> list, Function<? super T, ? extends R> mapper) {
        if (CollectionUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return list.stream().map(mapper).map(m -> {
                    if (m instanceof String) {
                        return new BigDecimal((String) m);
                    } else if (m instanceof Double) {
                        return BigDecimal.valueOf((Double) m);
                    } else if (m instanceof Integer) {
                        return BigDecimal.valueOf((Integer) m);
                    } else if (m instanceof Long) {
                        return BigDecimal.valueOf((Long) m);
                    }
                    return (BigDecimal) m;
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    public static <T, R> BigDecimal computeMapperTotal(List<T> list, Function<? super T, ? extends R> mapper, int scale) {
        if (CollectionUtil.isEmpty(list)) {
            return BigDecimal.ZERO;
        }
        return computeMapperTotal(list, mapper).setScale(scale, RoundingMode.HALF_UP);
    }

    /**
     * 判断为空时返回空List集合
     *
     * @param list
     * @return
     */
    public static <T> List<T> defaultValue(List<T> list) {
        return CollectionUtil.isNotEmpty(list) ? list : CollectionUtil.newArrayList();
    }

    public static <T> List<T> defaultValue(List<T> list, List<T> defaultValue) {
        return CollectionUtil.isNotEmpty(list) ? list : defaultValue(defaultValue);
    }

    /**
     * 判断为空时返回空Map集合
     *
     * @param map
     * @return
     */
    public static <K, V> Map<K, V> defaultValue(Map<K, V> map) {
        return MapUtil.isNotEmpty(map) ? map : MapUtil.newHashMap();
    }

    public static <K, V> Map<K, V> defaultValue(Map<K, V> map, Map<K, V> defaultValue) {
        return MapUtil.isNotEmpty(map) ? map : defaultValue(defaultValue);
    }

    public static List<String> commaJointNameToList(List<String> commaJointNameList) {
        if (CollectionUtil.isEmpty(commaJointNameList)) {
            return new ArrayList<>();
        }
        Set<String> set = new HashSet<>();
        for (String commaJointName : commaJointNameList) {
            List<String> list = commaJointNameToList(commaJointName);
            set.addAll(list);
        }
        return new ArrayList<>(set);
    }

    public static List<String> commaJointNameToList(String commaJointName) {
        return commaJointNameToList(commaJointName, StrUtil.COMMA);
    }

    public static List<String> commaJointNameToList(String commaJointName, String delimiter) {
        List<String> list = new ArrayList<>();
        if (StrUtil.isBlank(commaJointName)) {
            return list;
        }
        if (commaJointName.contains(delimiter)) {
            list = Arrays.stream(commaJointName.split(delimiter)).collect(Collectors.toList());
        } else {
            list.add(commaJointName);
        }

        return list;
    }

    public static String namesToIdsStr(String commaJointStrIds, Map<String, Long> nameIdMap) {
        return namesToIdsStr(commaJointStrIds, nameIdMap, StrUtil.COMMA);
    }

    public static String namesToIdsStr(String commaJointStrIds, Map<String, Long> nameIdMap, String delimiter) {
        List<String> nameList = CollectionUtils
                .commaJointNameToList(commaJointStrIds);
        if (CollectionUtil.isEmpty(nameList)) {
            return StrUtil.EMPTY;
        }

        return nameList.stream().map(name -> nameIdMap.getOrDefault(name, null))
                .filter(ObjectUtil::isNotEmpty).map(String::valueOf).collect(Collectors.joining(delimiter));
    }


    public static List<Long> commaJointStrIdsToList(List<String> commaJointStrList) {
        if (CollectionUtil.isEmpty(commaJointStrList)) {
            return new ArrayList<>();
        }
        Set<Long> set = new HashSet<>();
        for (String commaJointStr : commaJointStrList) {
            List<Long> list = commaJointStrIdsToList(commaJointStr);
            set.addAll(list);
        }
        return new ArrayList<>(set);
    }

    /**
     * 逗号拼接的IdString转换成Long类型的List集合
     *
     * @param commaJointStr
     * @return
     */
    public static List<Long> commaJointStrIdsToList(String commaJointStr) {
        return commaJointStrIdsToList(commaJointStr, StrUtil.COMMA);
    }

    public static List<Long> commaJointStrIdsToList(String commaJointStr, String delimiter) {
        List<Long> list = new ArrayList<>();
        if (StrUtil.isBlank(commaJointStr)) {
            return list;
        }
        if (commaJointStr.contains(delimiter)) {
            list = Arrays.stream(commaJointStr.split(delimiter)).map(Long::valueOf).collect(Collectors.toList());
        } else {
            list.add(Long.valueOf(commaJointStr));
        }

        return list;
    }

    /**
     * 过滤出重复的元素集合
     **/
    public static <T> List<T> findDuplicates(List<T> elements) {
        if (CollectionUtil.isEmpty(elements)) {
            return new ArrayList<>(0);
        }
        return elements.stream()
                .collect(Collectors.groupingBy(e -> e))
                .entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }

    public static String idsStrToNamesStr(String commaJointStrIds, Map<Long, String> idNameMap) {
        return idsStrToNamesStr(commaJointStrIds, idNameMap, StrUtil.COMMA);
    }

    public static String idsStrToNamesStr(String commaJointStrIds, Map<Long, String> idNameMap, String delimiter) {
        List<Long> idList = CollectionUtils
                .commaJointStrIdsToList(commaJointStrIds);
        if (CollectionUtil.isEmpty(idList)) {
            return StrUtil.EMPTY;
        }

        return idList.stream().map(id -> idNameMap.getOrDefault(id, StrUtil.EMPTY))
                .filter(StrUtil::isNotBlank).collect(Collectors.joining(delimiter));
    }

    public static <T> List<T> idsStrToEntityList(String commaJointStrIds, Map<Long, T> idEntityMap) {
        return idsStrToEntityList(commaJointStrIds, idEntityMap, StrUtil.COMMA);
    }

    public static <T> List<T> idsStrToEntityList(String commaJointStrIds, Map<Long, T> idEntityMap, String delimiter) {
        List<Long> idList = CollectionUtils
                .commaJointStrIdsToList(commaJointStrIds, delimiter);
        if (CollectionUtil.isEmpty(idList)) {
            return new ArrayList<>(0);
        }

        return idList.stream().map(id -> idEntityMap.getOrDefault(id, null))
                .filter(ObjectUtil::isNotEmpty).collect(Collectors.toList());
    }
}
